ποΈGitΠ―ΡΠ°ποΈ
desktopApp/README.md bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 9.21 KB
Tc9d1d9# `:desktopApp` β Meshtastic Desktop
A Compose Desktop application target β the first full non-Android target for the shared KMP module graph. This module serves as:
Tff7b721. **First multi-target milestone** β Proves the KMP architecture supports real application targets beyond Android.
Tff7b722. **Build smoke-test** β Validates that all Ta5d6ff`core:*` KMP modules compile and link on a JVM Desktop target.
Tff7b723. **Shared navigation proof** β Uses the same Navigation 3 routes from Ta5d6ff`core:navigation` and the same Ta5d6ff`NavDisplay` + Ta5d6ff`entryProvider` pattern as the Android app, proving the shared backstack architecture works cross-target.
Tff7b724. **Desktop app scaffold** β A working Compose Desktop application with a Ta5d6ff`NavigationRail` for top-level destinations and placeholder screens for each feature.
Tc9d1d9## Quick Start
Ta5d6ff```Ta5d6ffbash
T8b949e# Run the desktop app
./gradlew :desktopApp:run
T8b949e# Run tests
./gradlew :desktopApp:test
T8b949e# Package native distribution (DMG/MSI/DEB) β debug (no ProGuard)
./gradlew :desktopApp:packageDistributionForCurrentOS
T8b949e# Package native distribution (DMG/MSI/DEB) β release (ProGuard minified)
./gradlew :desktopApp:packageReleaseDistributionForCurrentOS
Ta5d6ff```
Tc9d1d9## Install via Homebrew (macOS, Apple Silicon)
Ta5d6ff```Ta5d6ffbash
brew install --cask meshtastic/tap/meshtastic-desktop
Ta5d6ff```
The cask lives in the [Tff7b72meshtastic/homebrew-tap](Te6edf3https://github.com/meshtastic/homebrew-tap) tap (alongside the Ta5d6ff`meshtasticd` formula) and is regenerated from the template at Ta5d6ff`.github/homebrew/meshtastic-desktop.rb` by the Ta5d6ff`update-homebrew-cask` job in Ta5d6ff`promote.yml`, which opens a PR against the tap for production releases when Ta5d6ff`HOMEBREW_TAP_TOKEN` is configured.
Tc9d1d9## ProGuard / Minification
Release builds use ProGuard for tree-shaking (unused code removal), significantly reducing distribution size. Obfuscation is disabled since the project is open-source. Rules are aligned with the Android R8 rules in Ta5d6ff`androidApp/proguard-rules.pro` β both targets share the same anti-class-merging philosophy.
**Configuration:**
Tff7b72- Ta5d6ff`build.gradle.kts` β Ta5d6ff`buildTypes.release.proguard` block enables ProGuard with Ta5d6ff`optimize.set(true)` and Ta5d6ff`obfuscate.set(false)`.
Tff7b72- Ta5d6ff`proguard-rules.pro` β Keep-rules for reflection/JNI-sensitive dependencies (Koin, kotlinx-serialization, Wire protobuf, Room KMP Ta5d6ff`androidx.room3`, Ktor, Kable BLE, Coil, SQLite JNI, Compose Multiplatform resources) and an anti-merge rule for Compose animation classes.
**Key rules:**
Tff7b72- **Compose animation anti-merge** (Ta5d6ff`-keep class androidx.compose.animation.** { *; }`) β Prevents ProGuard's optimizer from incorrectly tree-shaking or merging animation class hierarchies (e.g. Ta5d6ff`EnterTransition`/Ta5d6ff`ExitTransition` into Ta5d6ff`*Impl`), which causes animations to silently snap. Same rule as Android.
Tff7b72- **Room KMP** β Uses Ta5d6ff`androidx.room3` package path (Room KMP 3.x).
**Troubleshooting ProGuard issues:**
Tff7b72- If the release build crashes at runtime with Ta5d6ff`ClassNotFoundException` or Ta5d6ff`NoSuchMethodError`, a library is loading classes via reflection that ProGuard stripped. Add a Ta5d6ff`-keep` rule in Ta5d6ff`proguard-rules.pro` **and** the corresponding rule in Ta5d6ff`androidApp/proguard-rules.pro` to keep both targets aligned.
Tff7b72- To debug which classes ProGuard removes, temporarily add Ta5d6ff`-printusage proguard-usage.txt` to the rules file and inspect the output in Ta5d6ff`desktopApp/proguard-usage.txt`.
Tff7b72- To see the full mapping of optimizations applied, add Ta5d6ff`-printseeds proguard-seeds.txt`.
Tff7b72- Run Ta5d6ff`./gradlew :desktopApp:runRelease` for a quick smoke-test of the minified app before packaging.
Tc9d1d9## Architecture
The module depends on the JVM variants of KMP modules:
Tff7b72- Ta5d6ff`core:common`, Ta5d6ff`core:model`, Ta5d6ff`core:di`, Ta5d6ff`core:navigation`, Ta5d6ff`core:repository`
Tff7b72- Ta5d6ff`core:domain`, Ta5d6ff`core:data`, Ta5d6ff`core:database`, Ta5d6ff`core:datastore`, Ta5d6ff`core:prefs`
Tff7b72- Ta5d6ff`core:network`, Ta5d6ff`core:resources`, Ta5d6ff`core:ui`
**Navigation:** Uses JetBrains multiplatform forks of Navigation 3 (Ta5d6ff`org.jetbrains.androidx.navigation3:navigation3-ui`) and Lifecycle (Ta5d6ff`org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose`, Ta5d6ff`lifecycle-runtime-compose`). A unified Ta5d6ff`SavedStateConfiguration` with polymorphic Ta5d6ff`SerializersModule` is provided centrally by Ta5d6ff`core:navigation` for non-Android NavKey serialization. Desktop utilizes the exact same navigation graph wiring (Ta5d6ff`settingsGraph`, Ta5d6ff`nodesGraph`, Ta5d6ff`contactsGraph`, Ta5d6ff`connectionsGraph`) directly from the Ta5d6ff`commonMain` of their respective feature modules, maintaining full UI parity.
**Coroutines:** Requires Ta5d6ff`kotlinx-coroutines-swing` for Ta5d6ff`Dispatchers.Main` on JVM/Desktop. Without it, any code using Ta5d6ff`lifecycle.coroutineScope` or Ta5d6ff`Dispatchers.Main` (e.g., Ta5d6ff`NodeRepositoryImpl`, Ta5d6ff`RadioConfigRepositoryImpl`) will crash at runtime.
**DI:** A Koin DI graph is bootstrapped in Ta5d6ff`Main.kt` with platform-specific implementations injected.
**UI:** JetBrains Compose for Desktop with Material 3 theming. Desktop acts as a thin host shell, delegating almost entirely to fully shared KMP UI modules. Includes native OS notifications (macOS Ta5d6ff`UNUserNotificationCenter`, Linux libnotify, Windows toast) and a monochrome SVG tray icon for a native look and feel.
**Notifications:** Implements the common Ta5d6ff`NotificationManager` interface via Ta5d6ff`DesktopNotificationManager`. Repository-level notifications (messages, node events, alerts) are collected in Ta5d6ff`Main.kt` and forwarded to platform-native senders; the app falls back to Compose Ta5d6ff`TrayState` notifications only if native delivery fails. macOS requires a consistent Ta5d6ff`bundleID` (configured in Ta5d6ff`build.gradle.kts`) for proper app attribution.
**Localization:** Desktop exposes a language picker, persisting the selected BCP-47 tag in Ta5d6ff`UiPrefs.locale` (Ta5d6ff`Main.kt` injects Ta5d6ff`org.meshtastic.core.repository.UiPrefs`). Ta5d6ff`Main.kt` applies the override to the JVM default Ta5d6ff`Locale` and uses a Ta5d6ff`staticCompositionLocalOf`-backed recomposition trigger so Compose Multiplatform Ta5d6ff`stringResource()` calls update immediately without recreating the Navigation 3 backstack.
Tc9d1d9## Key Files
| File | Purpose |
|---|---|
| Ta5d6ff`Main.kt` | App entry point β Koin bootstrap, Compose Desktop window, theme + locale application |
| Ta5d6ff`ui/DesktopMainScreen.kt` | Navigation 3 shell β Ta5d6ff`NavigationRail` + Ta5d6ff`NavDisplay` |
| Ta5d6ff`navigation/DesktopNavigation.kt` | Nav graph entry registrations for all top-level destinations (delegates to shared feature graphs) |
| Ta5d6ff`radio/DesktopRadioTransportFactory.kt` | Provides TCP, Serial/USB, and BLE transports |
| Ta5d6ff`notification/DesktopMeshNotificationManager.kt` | Desktop Ta5d6ff`MeshNotificationManager` β converts mesh events into Ta5d6ff`Notification`s and dispatches them |
| Ta5d6ff`notification/NativeNotificationSender.kt` | Native-sender contract, implemented by Ta5d6ff`MacOSNotificationSender` / Ta5d6ff`WindowsNotificationSender` / Ta5d6ff`LinuxNotificationSender` |
| Ta5d6ff`DesktopNotificationManager.kt` | Bridge between repository notifications and Compose Ta5d6ff`TrayState` |
| Ta5d6ff`radio/DesktopMessageQueue.kt` | Message queue for outbound mesh packets |
| Ta5d6ff`di/DesktopKoinModule.kt` | Koin module with stub implementations |
| Ta5d6ff`di/DesktopPlatformModule.kt` | Platform-specific Koin bindings |
| Ta5d6ff`stub/NoopStubs.kt` | No-op implementations for all repository interfaces |
Tc9d1d9## What This Validates
| Module | What's Tested |
|---|---|
| Ta5d6ff`core:common` | Ta5d6ff`NumberFormatter`, Ta5d6ff`UrlUtils`, Ta5d6ff`DateFormatter`, Ta5d6ff`CommonUri` |
| Ta5d6ff`core:model` | Ta5d6ff`DeviceVersion`, Ta5d6ff`Capabilities`, Ta5d6ff`SfppHasher`, Ta5d6ff`platformRandomBytes`, Ta5d6ff`getShortDateTime`, Ta5d6ff`Channel.getRandomKey` |
| Ta5d6ff`core:ui` | Shared Compose components compile and render on Desktop |
| Build graph | All core modules compile and link without Android SDK |
Tc9d1d9## Roadmap
Tff7b72- Tff7b72[x] Implement real navigation with shared Ta5d6ff`core:navigation` routes (Navigation 3 shell)
Tff7b72- Tff7b72[x] Adopt JetBrains multiplatform forks for lifecycle and navigation3
Tff7b72- Tff7b72[x] Implement native macOS/Desktop notification support with Ta5d6ff`TrayState` and system tray
Tff7b72- Tff7b72[x] Wire Ta5d6ff`feature:settings` composables into the nav graph (first real feature β ~30 screens)
Tff7b72- Tff7b72[x] Wire Ta5d6ff`feature:node` composables into the nav graph (node list with shared ViewModel + NodeItem)
Tff7b72- Tff7b72[x] Wire Ta5d6ff`feature:messaging` composables into the nav graph (contacts list with shared ViewModel)
Tff7b72- Tff7b72[x] Add JetBrains Material 3 Adaptive Ta5d6ff`ListDetailPaneScaffold` to node and messaging screens
Tff7b72- Tff7b72[x] Implement TCP transport (Ta5d6ff`DesktopRadioTransportFactory`) with auto-reconnect and backoff retry
Tff7b72- Tff7b72[x] Implement mesh service lifecycle with full Ta5d6ff`want_config` handshake (lives in shared Ta5d6ff`:core:service` β Ta5d6ff`SharedRadioInterfaceService` / Ta5d6ff`MeshServiceOrchestrator`)
Tff7b72- Tff7b72[x] Create connections screen using shared Ta5d6ff`feature:connections` with dynamic transport detection
Tff7b72- Tff7b72[x] Replace 5 placeholder config screens with real desktop implementations (Device, Position, Network, Security, ExtNotification)
Tff7b72- Tff7b72[x] Add desktop language picker backed by shared Ta5d6ff`UiPrefs.locale` with live translation updates
Tff7b72- Tff7b72[x] Wire remaining Ta5d6ff`feature:*` composables (map) into the nav graph
Tff7b72- Tff7b72[x] Move remaining node detail and message composables from Ta5d6ff`androidMain` to Ta5d6ff`commonMain`
Tff7b72- Tff7b72[x] Add serial/USB transport for direct radio connection on Desktop
Tff7b72- Tff7b72[x] Add BLE transport (via Kable) for direct radio connection on Desktop
Tff7b72- Tff7b72[x] Add MQTT transport for cloud-connected operation
Tff7b72- Tff7b72[x] Package as native distributions (DMG, MSI, DEB) via CI release pipeline
Served by rngit 1.5.4 - Generated in 0.04s